package cn.smile.business.core.impl;

import cn.smile.bean.dto.core.RoleInfoDTO;
import cn.smile.bean.entity.core.RoleInfo;
import cn.smile.bean.entity.core.UserRole;
import cn.smile.bean.form.core.role.RoleAddForm;
import cn.smile.bean.form.core.role.RoleQueryForm;
import cn.smile.bean.form.core.role.RoleRelUpdateForm;
import cn.smile.bean.form.core.role.RoleUpdateForm;
import cn.smile.business.core.IResService;
import cn.smile.business.core.IRoleService;
import cn.smile.commons.constant.CommonConstant;
import cn.smile.commons.constant.NumberConstant;
import cn.smile.commons.constant.RedisKeyConstant;
import cn.smile.commons.domain.BaseServiceImpl;
import cn.smile.commons.exceptions.BusinessException;
import cn.smile.commons.response.MyResponseCode;
import cn.smile.repository.core.RoleInfoMapper;
import cn.smile.repository.core.UserRoleMapper;
import cn.smile.utils.BeanCopy;
import cn.smile.utils.RedisUtil;
import cn.smile.utils.UserUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import jakarta.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author smile-jt
 */
@Slf4j
@Service
public class RoleServiceImpl extends BaseServiceImpl<RoleInfoMapper, RoleInfo> implements IRoleService {
    private static final String DEFAULT_WORK_SPACE = "A001";
    private static final String DEFAULT_ANALYSIS_PAGE = "A002";
    private static final String DEFAULT_HOME_PAGE = "/";
    @Resource
    private UserRoleMapper relMapper;
    @Resource
    private IResService resService;
    @Resource
    private RedisUtil redisUtil;

    @Override
    public boolean checkCode(String roleCode) {
        log.info("[RoleServiceImpl].[checkCode] ------> Check Role Code Is Exist Start, roleCode = {}", roleCode);
        boolean result = false;
        if (!ObjectUtils.isEmpty(roleCode)) {
            List<RoleInfo> dataList = baseMapper.selectList(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE).eq(RoleInfo::getRoleCode, roleCode));
            result = !CollectionUtils.isEmpty(dataList);
        }
        log.info("[RoleServiceImpl].[checkCode] ------> Check Role Code Is Exist End, result = {}", result);
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addInfo(RoleAddForm form) {
        log.info("[RoleServiceImpl].[addInfo] Add Role Info Start, form = {}", JSON.toJSONString(form));
        if (this.checkCode(form.getRoleCode())) {
            throw new BusinessException(MyResponseCode.ROLE_CODE_EXIST_ERROR);
        }
        boolean ruleCheck = CommonConstant.checkRule(form.getRoleCode(), CommonConstant.ROLE_CODE_RULE);
        if (!ruleCheck) {
            throw new BusinessException(MyResponseCode.ROLE_CODE_INCONFORMITY_RULE);
        }
        Integer mxNum = baseMapper.selectMaxSort();
        RoleInfo entity = BeanCopy.copyBean(form, RoleInfo.class);
        entity.setSortNum(mxNum + NumberConstant.ONE);
        super.addBaseCol(entity);
        entity.setPredefined(Boolean.FALSE);
        int insertNum = baseMapper.insert(entity);
        if (insertNum > NumberConstant.ZERO) {
            this.resService.saveResByRole(form.getMenuCodes(), entity.getId());
        }
        log.info("[RoleServiceImpl].[addInfo] Add Role Info End, insertNum = {}", insertNum);
        return insertNum > NumberConstant.ZERO;
    }

    @Override
    public Boolean updateInfo(RoleUpdateForm form) {
        log.info("[RoleServiceImpl].[updateInfo] 更新角色信息 Start, form = {}", JSON.toJSONString(form));
        //默认角色Code不可修改
        form.setRoleCode(null);
        RoleInfo entity = BeanCopy.copyBean(form, RoleInfo.class);
        super.updateBaseCol(entity);
        int updateNum = baseMapper.updateById(entity);
        if (updateNum > NumberConstant.ZERO) {
            this.resService.saveResByRole(form.getMenuCodes(), entity.getId());
        }
        log.info("[RoleServiceImpl].[updateInfo] 更新角色信息 End, updateNum = {}", updateNum);
        return updateNum > NumberConstant.ZERO;
    }

    @Override
    public List<RoleInfoDTO> queryList(RoleQueryForm form) {
        log.info("[RoleServiceImpl].[queryList] ------> Get Role Info List Start, form = {}", JSON.toJSONString(form));
        //该接口默认查询全部角色
        form.setShow(null);
        LambdaQueryWrapper<RoleInfo> qw = this.getQueryParams(form);
        List<RoleInfo> dataList = baseMapper.selectList(qw);
        List<RoleInfoDTO> result = BeanCopy.copyList(dataList, RoleInfoDTO.class);
        log.info("[RoleServiceImpl].[queryList] ------> Get Role Info List End");
        return result;
    }

    @Override
    public IPage<RoleInfoDTO> queryPage(RoleQueryForm form) {
        log.info("[RoleServiceImpl].[queryPage] ------> Get Role Info Page Start, form = {}", JSON.toJSONString(form));
        LambdaQueryWrapper<RoleInfo> qw = this.getQueryParams(form);
        IPage<RoleInfo> dataPage = baseMapper.selectPage(form.queryPage(), qw);
        IPage<RoleInfoDTO> result = BeanCopy.copyPage(dataPage, RoleInfoDTO.class);
        this.setRoleRes(result.getRecords());
        log.info("[RoleServiceImpl].[queryPage] ------> Get Role Info Page End");
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateRel(RoleRelUpdateForm form) {
        log.info("[RoleServiceImpl].[updateRel] ------> Update Role User Relation Start, form = {}", JSON.toJSONString(form));
        boolean result = true;
        List<UserRole> userRoles = relMapper.selectList(new QueryWrapper<UserRole>().lambda().eq(UserRole::getDeleted, Boolean.FALSE).eq(UserRole::getRoleId, form.getUserId()).eq(UserRole::getRoleId, form.getRoleId()));
        if (form.getDel()) {
            for (UserRole entity : userRoles) {
                entity.setDeleted(Boolean.TRUE);
                relMapper.updateById(entity);
            }
        } else {
            if (CollectionUtils.isEmpty(userRoles)) {
                UserRole userRole = BeanCopy.copyBean(form, UserRole.class);
                userRole.setDeleted(Boolean.FALSE);
                userRole.setCreateTime(LocalDateTime.now());
                userRole.setUpdateTime(LocalDateTime.now());
                relMapper.insert(userRole);
            }
        }
        log.info("[RoleServiceImpl].[updateRel] ------> Update Role User Relation End, result = {}", result);
        return result;
    }

    @Override
    public RoleInfoDTO getInfoById(String id) {
        log.info("[RoleServiceImpl].[getInfoById] ------> Query Role Info By Id Start, id = {}", id);
        RoleInfo roleInfo = baseMapper.selectOne(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE).eq(RoleInfo::getId, id));
        RoleInfoDTO entity = BeanCopy.copyBean(roleInfo, RoleInfoDTO.class);
        log.info("[RoleServiceImpl].[getInfoById] ------> Query Role Info By Id End");
        return entity;
    }

    @Override
    public List<RoleInfoDTO> queryRole(List<String> role) {
        log.info("[RoleServiceImpl].[queryRole] ------> Query Admin Role Info Start");
        List<RoleInfo> roleInfo = baseMapper.selectList(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE).in(RoleInfo::getRoleCode, role));
        List<RoleInfoDTO> dtoList = BeanCopy.copyList(roleInfo, RoleInfoDTO.class);
        log.info("[RoleServiceImpl].[queryRole] ------> Query Admin Role Info End");
        return dtoList;
    }

    @Override
    public Boolean initUser(String userId) {
        log.info("[RoleServiceImpl].[initUser] ------> 初始化用户角色关系 Start, userId = {}", userId);
        RoleInfo role = baseMapper.selectOne(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE).eq(RoleInfo::getRoleCode, CommonConstant.DEFAULT_USER_ROLE_CODE));
        UserRole userRole = new UserRole();
        userRole.setUserId(userId);
        userRole.setRoleId(role.getId());
        userRole.setDeleted(Boolean.FALSE);
        userRole.setCreateTime(LocalDateTime.now());
        userRole.setUpdateTime(LocalDateTime.now());
        int insertNum = relMapper.insert(userRole);
        log.info("[RoleServiceImpl].[initUser] ------> 初始化用户角色关系 End, insertNum = {}", insertNum);
        return insertNum > NumberConstant.ZERO;
    }

    @Override
    public void writeToRedis() {
        log.info("[RoleServiceImpl].[queryAdminRole] ------> Query Admin Role Info Start");
        List<RoleInfo> roles = baseMapper.selectList(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE));
        if (!CollectionUtils.isEmpty(roles)) {
            for (RoleInfo info : roles) {
                Set<String> uris = resService.queryUriByRoleIds(Lists.newArrayList(info.getRoleCode()));
                redisUtil.set(String.format(RedisKeyConstant.ROLE_URLS_KEY, info.getRoleCode()), JSON.toJSONString(uris), RedisKeyConstant.ROLE_URLS_TIMEOUT);
            }
        }
        log.info("[RoleServiceImpl].[queryAdminRole] ------> Query Admin Role Info End");
    }

    @Override
    public List<String> getRouteCodeByLogin() {
        log.info("[RoleServiceImpl].[getRouteCodeByLogin] ------> 查询登录用户路由 Start");
        List<String> codeList = Lists.newArrayList();
        codeList.add(DEFAULT_WORK_SPACE);
        codeList.add(DEFAULT_ANALYSIS_PAGE);
        codeList.add(DEFAULT_HOME_PAGE);
        List<RoleInfoDTO> roles = this.getLoginRoles();
        if (!CollectionUtils.isEmpty(roles)) {
            List<String> roleIds = roles.stream().map(RoleInfoDTO::getId).collect(Collectors.toList());
            Set<String> menuCodeList = resService.getMenuCodeByRole(roleIds);
            if (!CollectionUtils.isEmpty(menuCodeList)) {
                codeList.addAll(Lists.newArrayList(menuCodeList));
            }
        }
        log.info("[RoleServiceImpl].[getRouteCodeByLogin] ------> 查询登录用户路由 End");
        return codeList;
    }

    @Override
    public List<RoleInfoDTO> getLoginRoles() {
        log.info("[RoleServiceImpl].[getLoginRoles] ------> 查询登录用户角色集合 Start");
        List<RoleInfoDTO> dataList = Lists.newArrayList();
        List<UserRole> userRoles = relMapper.selectList(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, UserUtil.getUserInfo().getId()).eq(UserRole::getDeleted, Boolean.FALSE));
        if (!CollectionUtils.isEmpty(userRoles)) {
            List<String> roleIds = userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList());
            List<RoleInfo> roleInfos = baseMapper.selectList(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE).in(RoleInfo::getId, roleIds));
            dataList = BeanCopy.copyList(roleInfos, RoleInfoDTO.class);
        }
        log.info("[RoleServiceImpl].[getLoginRoles] ------> 查询登录用户角色集合 End");
        return dataList;
    }

    @Override
    public Map<String, Set<String>> getRoleCodeMap(List<String> userIds) {
        log.info("[RoleServiceImpl].[getRoleCodeMap] ------> 查询用户角色集合 Start");
        Map<String, Set<String>> roleCodeMap = Maps.newHashMap();
        List<UserRole> userRoles = relMapper.selectList(new QueryWrapper<UserRole>().lambda().in(UserRole::getUserId, userIds).eq(UserRole::getDeleted, Boolean.FALSE));
        if (!CollectionUtils.isEmpty(userRoles)) {
            List<String> roleIds = userRoles.stream().map(UserRole::getRoleId).collect(Collectors.toList());
            Map<String, RoleInfo> roleMap = Maps.newHashMap();
            List<RoleInfo> roleInfos = baseMapper.selectList(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE).in(RoleInfo::getId, roleIds));
            if (!CollectionUtils.isEmpty(roleInfos)) {
                roleInfos.forEach(role -> roleMap.put(role.getId(), role));
            }
            for (UserRole entity : userRoles) {
                if (!roleCodeMap.containsKey(entity.getUserId())) {
                    roleCodeMap.put(entity.getUserId(), new HashSet<>(NumberConstant.EIGHT));
                }
                if (roleMap.containsKey(entity.getRoleId())) {
                    roleCodeMap.get(entity.getUserId()).add(roleMap.get(entity.getRoleId()).getRoleCode());
                }
            }
        }
        log.info("[RoleServiceImpl].[getRoleCodeMap] ------> 查询用户角色集合 End");
        return roleCodeMap;
    }

    @Override
    public void saveRoleByUser(List<String> roleCodes, String userId) {
        log.info("[RoleServiceImpl].[saveRoleByUser] ------> 保存用户关联角色 Start, roleCodes = {}, userId = {}", JSON.toJSONString(roleCodes), userId);
        //不管新角色是什么, 先删除原来的角色
        relMapper.delete(new QueryWrapper<UserRole>().lambda().eq(UserRole::getUserId, userId));
        if (!CollectionUtils.isEmpty(roleCodes)) {
            List<RoleInfo> roleInfos = baseMapper.selectList(new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE).in(RoleInfo::getRoleCode, roleCodes));
            if (!CollectionUtils.isEmpty(roleInfos)) {
                roleInfos.forEach(role -> {
                    UserRole userRole = new UserRole();
                    userRole.setUserId(userId);
                    userRole.setRoleId(role.getId());
                    userRole.setDeleted(Boolean.FALSE);
                    userRole.setCreateTime(LocalDateTime.now());
                    userRole.setUpdateTime(LocalDateTime.now());
                    relMapper.insert(userRole);
                });
            }
        }
        log.info("[RoleServiceImpl].[saveRoleByUser] ------> 保存用户关联角色 End");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean delById(String id) {
        log.info("[RoleServiceImpl].[delById] ------> Delete Role Info By Id Start, id = {}", id);
        RoleInfoDTO entity = this.getInfoById(id);
        if (entity.getPredefined()) {
            throw new BusinessException(MyResponseCode.PREDEFINED_ROLE_ERROR);
        }
        entity.setDeleted(Boolean.TRUE);
        int updateNum = baseMapper.updateById(entity);
        if (updateNum > NumberConstant.ZERO) {
            relMapper.delete(new QueryWrapper<UserRole>().lambda().eq(UserRole::getRoleId, id));
        }
        log.info("[RoleServiceImpl].[delById] ------> Delete Role Info By Id End, updateNum = {}", updateNum);
        return updateNum > NumberConstant.ZERO;
    }

    /**
     * 获取查询条件
     *
     * @param form 查询参数
     */
    private LambdaQueryWrapper<RoleInfo> getQueryParams(RoleQueryForm form) {
        LambdaQueryWrapper<RoleInfo> qw = new QueryWrapper<RoleInfo>().lambda().eq(RoleInfo::getDeleted, Boolean.FALSE);
        if (!ObjectUtils.isEmpty(form.getUserId())) {
            List<UserRole> userRoles = relMapper.selectList(new QueryWrapper<UserRole>().lambda().eq(UserRole::getDeleted, Boolean.FALSE).eq(UserRole::getUserId, form.getUserId()));
            List<String> ids = Lists.newArrayList();
            if (!CollectionUtils.isEmpty(userRoles)) {
                userRoles.forEach(entity -> ids.add(entity.getRoleId()));
            } else {
                ids.add(UUID.randomUUID().toString());
            }
            qw.in(RoleInfo::getId, ids);
        }
        if (!CollectionUtils.isEmpty(form.getRoleCodes())) {
            qw.in(RoleInfo::getRoleCode, form.getRoleCodes());
        }
        if (!ObjectUtils.isEmpty(form.getShow())) {
            qw.in(RoleInfo::getShow, form.getShow());
        }
        if (!ObjectUtils.isEmpty(form.getRoleName())) {
            qw.like(RoleInfo::getRoleName, form.getRoleName());
        }
        return qw;
    }

    /**
     * 写入角色资源Code集合
     *
     * @param dataList 角色集合
     */
    private void setRoleRes(List<RoleInfoDTO> dataList) {
        if (!CollectionUtils.isEmpty(dataList)) {
            List<String> roleIds = dataList.stream().map(RoleInfoDTO::getId).collect(Collectors.toList());
            Map<String, List<String>> resCodeMap = resService.getResCodeMap(roleIds);
            for (RoleInfoDTO dto : dataList) {
                dto.setResCodeList(resCodeMap.getOrDefault(dto.getId(), Lists.newArrayList()));
            }
        }
    }
}
